home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / packer / tar / src / extract.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  14KB  |  540 lines

  1. /*
  2.  * Extract files from a tar archive.
  3.  *
  4.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  5.  *
  6.  * @(#) extract.c 1.32 87/11/11 Public Domain - gnu
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13.  
  14. #ifdef BSD42
  15. #include <sys/file.h>
  16. #endif
  17.  
  18. #ifdef USG
  19. #include <fcntl.h>
  20. #endif
  21.  
  22. #if defined(MSDOS) || defined(AMIGA)
  23. #include <fcntl.h>
  24. #endif    /* MSDOS */
  25.  
  26. /*
  27.  * Some people don't have a #define for these.
  28.  */
  29. #ifndef    O_BINARY
  30. #define    O_BINARY    0
  31. #endif
  32. #ifndef O_NDELAY
  33. #define    O_NDELAY    0
  34. #endif
  35.  
  36. #ifdef NO_OPEN3
  37. /* We need the #define's even though we don't use them. */
  38. #include "open3.h"
  39. #endif
  40.  
  41. #ifdef EMUL_OPEN3
  42. /* Simulated 3-argument open for systems that don't have it */
  43. #include "open3.h"
  44. #endif
  45.  
  46. extern int errno;            /* From libc.a */
  47. extern time_t time();            /* From libc.a */
  48. extern char *index();            /* From libc.a or port.c */
  49.  
  50. #include "tar.h"
  51. #include "port.h"
  52.  
  53. extern union record *head;        /* Points to current tape header */
  54. extern struct stat hstat;        /* Stat struct corresponding */
  55. extern int head_standard;        /* Tape header is in ANSI format */
  56.  
  57. extern void print_header();
  58. extern void skip_file();
  59. extern void pr_mkdir();
  60.  
  61. int make_dirs();            /* Makes required directories */
  62. #ifdef AMIGA
  63. extern int made_on_amiga;        /* decode_header() sets this if tar
  64.                      * was made on Amiga.  Passing this
  65.                      * as a param would change too much
  66.                      * code so we make it a global */
  67. #endif
  68.  
  69. static time_t now = 0;            /* Current time */
  70. static we_are_root = 0;            /* True if our effective uid == 0 */
  71. static int notumask = ~0;        /* Masks out bits user doesn't want */
  72.  
  73. /*
  74.  * Set up to extract files.
  75.  */
  76. extr_init()
  77. {
  78.     int ourmask;
  79.  
  80.     now = time((time_t *)0);
  81.     if (geteuid() == 0)
  82.         we_are_root = 1;
  83.  
  84.     /*
  85.      * We need to know our umask.  But if f_use_protection is set,
  86.      * leave our kernel umask at 0, and our "notumask" at ~0.
  87.      */
  88.     ourmask = umask(0);        /* Read it */
  89.     if (!f_use_protection) {
  90.         (void) umask (ourmask);    /* Set it back how it was */
  91.         notumask = ~ourmask;    /* Make umask override permissions */
  92.     }
  93. }
  94.  
  95.  
  96. /*
  97.  * Extract a file from the archive.
  98.  */
  99. void
  100. extract_archive()
  101. {
  102.     register char *data;
  103.     int fd, check, namelen, written, openflag;
  104.     long size;
  105.     time_t acc_upd_times[2];
  106.     register int skipcrud;
  107. #ifdef AMIGA
  108.     int protection;        /* protection bits */
  109. #endif
  110.  
  111.     saverec(&head);            /* Make sure it sticks around */
  112.     userec(head);            /* And go past it in the archive */
  113.     decode_header(head, &hstat, &head_standard, 1);    /* Snarf fields */
  114.  
  115.     /* Print the record from 'head' and 'hstat' */
  116.     if (f_verbose)
  117.         print_header(stdout);
  118.  
  119.     /*
  120.      * Check for fully specified pathnames and other atrocities.
  121.      *
  122.      * Note, we can't just make a pointer to the new file name,
  123.      * since saverec() might move the header and adjust "head".
  124.      * We have to start from "head" every time we want to touch
  125.      * the header record.
  126.      */
  127.     skipcrud = 0;
  128.     while ('/' == head->header.name[skipcrud]) {
  129.         static int warned_once = 0;
  130.  
  131.         skipcrud++;    /* Force relative path */
  132.         if (!warned_once++) {
  133.             annorec(stderr, tar);
  134.             fprintf(stderr,
  135.     "Removing leading / from absolute path names in the archive.\n");
  136.         }
  137.     }
  138. #ifdef AMIGA
  139.     cvtUNIX2Ami(skipcrud + head->header.name);
  140. #endif
  141.     switch (head->header.linkflag) {
  142.  
  143.     default:
  144.         annofile(stderr, tar);
  145.         fprintf(stderr,
  146.            "Unknown file type '%c' for %s, extracted as normal file\n",
  147.             head->header.linkflag, skipcrud+head->header.name);
  148.         /* FALL THRU */
  149.  
  150.     case LF_OLDNORMAL:
  151.     case LF_NORMAL:
  152.     case LF_CONTIG:
  153.         /*
  154.          * Appears to be a file.
  155.          * See if it's really a directory.
  156.          */
  157.         namelen = strlen(skipcrud+head->header.name)-1;
  158.         if (head->header.name[skipcrud+namelen] == '/')
  159.             goto really_dir;
  160.  
  161.         /* FIXME, deal with protection issues */
  162.     again_file:
  163. #if defined(AMIGA) && defined(LATTICE)    /* open w/O_EXCL is broken, sigh */
  164.         openflag = O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC;
  165. #else
  166.         openflag = f_keep?
  167.             O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
  168.             O_BINARY|O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC;
  169. #endif
  170.  
  171. #ifdef O_CTG
  172.         /*
  173.          * Contiguous files (on the Masscomp) have to specify
  174.          * the size in the open call that creates them.
  175.          */
  176.         if (head->header.lnkflag == LF_CONTIG)
  177.             fd = open(skipcrud+head->header.name, openflag | O_CTG,
  178.                 hstat.st_mode, hstat.st_size);
  179.         else
  180. #endif
  181.         {
  182. #ifdef NO_OPEN3
  183.             /*
  184.              * On raw V7 we won't let them specify -k (f_keep), but
  185.              * we just bull ahead and create the files.
  186.              */
  187.             fd = creat(skipcrud+head->header.name, 
  188.                 hstat.st_mode);
  189. #else
  190.             /*
  191.              * With 3-arg open(), we can do this up right.
  192.              */
  193. #if defined(AMIGA) && defined(LATTICE)
  194.             if (f_keep && !access(skipcrud+head->header.name,
  195.                          O_RDONLY))
  196.             {
  197.                 fd = -1;
  198.                 errno = EEXIST;
  199.             }
  200.             else
  201.                 fd = open(skipcrud+head->header.name, openflag,
  202.                       hstat.st_mode);
  203. #endif
  204. #endif
  205.         }
  206.  
  207.         if (fd < 0) {
  208.             if (make_dirs(skipcrud+head->header.name))
  209.                 goto again_file;
  210.             annofile(stderr, tar);
  211.             fprintf(stderr, "Could not make file ");
  212.             perror(skipcrud+head->header.name);
  213.             skip_file((long)hstat.st_size);
  214.             goto quit;
  215.         }
  216.  
  217.         for (size = hstat.st_size;
  218.              size > 0;
  219.              size -= written) {
  220.             /*
  221.              * Locate data, determine max length
  222.              * writeable, write it, record that
  223.              * we have used the data, then check
  224.              * if the write worked.
  225.              */
  226.             data = findrec()->charptr;
  227.             if (data == NULL) {    /* Check it... */
  228.                 annorec(stderr, tar);
  229.                 fprintf(stderr, "Unexpected EOF on archive file\n");
  230.                 break;
  231.             }
  232.             written = endofrecs()->charptr - data;
  233.             if (written > size) written = size;
  234.             errno = 0;
  235.             check = write(fd, data, written);
  236.             /*
  237.              * The following is in violation of strict
  238.              * typing, since the arg to userec
  239.              * should be a struct rec *.  FIXME.
  240.              */
  241.             userec(data + written - 1);
  242.             if (check == written) continue;
  243.             /*
  244.              * Error in writing to file.
  245.              * Print it, skip to next file in archive.
  246.              */
  247.             annofile(stderr, tar);
  248.             fprintf(stderr,
  249.     "Tried to write %d bytes to file, could only write %d:\n",
  250.                 written, check);
  251.             perror(skipcrud+head->header.name);
  252.             skip_file((long)(size - written));
  253.             break;    /* Still do the close, mod time, chmod, etc */
  254.         }
  255.  
  256.         check = close(fd);
  257.         if (check < 0) {
  258.             annofile(stderr, tar);
  259.             fprintf(stderr, "Error while closing ");
  260.             perror(skipcrud+head->header.name);
  261.         }
  262.         
  263.     set_filestat:
  264. #ifdef AMIGA
  265.         /*
  266.          * Set the comment on the file
  267.          */
  268.         if (made_on_amiga)
  269.         {
  270.             if (SmartSetComment(skipcrud+head->header.name,
  271.                     hstat.st_comment) < 0)
  272.             {
  273.             annofile(stderr, tar);
  274.             fputs("failed to set comment on ", stderr);
  275.             perror(skipcrud+head->header.name);
  276.             }
  277.         }
  278. #endif
  279.         /*
  280.          * Set the modified time of the file.
  281.          * 
  282.          * Note that we set the accessed time to "now", which
  283.          * is really "the time we started extracting files".
  284.          */
  285.         if (!f_modified) {
  286.             acc_upd_times[0] = now;             /* Accessed now */
  287.             acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
  288. #ifdef AMIGA
  289.             if (made_on_amiga) {
  290.                 if (utime_from_stamp(skipcrud+head->header.name,
  291.                     &(hstat.st_date)) < 0) {
  292.                     annofile(stderr, tar);
  293.                     fputs("utime failed:", stderr);
  294.                     perror(skipcrud+head->header.name);
  295.                 }
  296.             } else {
  297. #endif
  298.             if (utime(skipcrud+head->header.name,
  299.                 acc_upd_times) < 0) {
  300.                 annofile(stderr, tar);
  301.                 fputs("utime failed:", stderr);
  302.                 perror(skipcrud+head->header.name);
  303.             }
  304. #ifdef AMIGA
  305.             }
  306. #endif
  307.         }
  308.  
  309.         /*
  310.          * If we are root, set the owner and group of the extracted
  311.          * file.  This does what is wanted both on real Unix and on
  312.          * System V.  If we are running as a user, we extract as that
  313.          * user; if running as root, we extract as the original owner.
  314.          */
  315.         if (we_are_root) {
  316.             if (chown(skipcrud+head->header.name, hstat.st_uid,
  317.                   hstat.st_gid) < 0) {
  318.                 annofile(stderr, tar);
  319.                 perror(skipcrud+head->header.name);
  320.             }
  321.         }
  322.  
  323.         /*
  324.          * If '-k' is not set, open() or creat() could have saved
  325.          * the permission bits from a previously created file,
  326.          * ignoring the ones we specified.
  327.          * Even if -k is set, if the file has abnormal
  328.          * mode bits, we must chmod since writing or chown() has
  329.          * probably reset them.
  330.          *
  331.          * If -k is set, we know *we* created this file, so the mode
  332.          * bits were set by our open().   If the file is "normal", we
  333.          * skip the chmod.  This works because we did umask(0) if -p
  334.          * is set, so umask will have left the specified mode alone.
  335.          */
  336. #ifdef AMIGA
  337.         /*
  338.          * open() ignores modes, so always do this unless
  339.          */
  340.         if (made_on_amiga)
  341.             protection = hstat.st_prot & ~FIBF_ARCHIVE;
  342.         else
  343.             protection = ~((hstat.st_mode >> 5) | 1) & 0xf;
  344.         if (SmartSetProtection(skipcrud+head->header.name,
  345.                        protection) < 0)
  346.         {
  347.             annofile(stderr, tar);
  348.             fputs("failed to set protection on ", stderr);
  349.             perror(skipcrud+head->header.name);
  350.         }
  351. #else
  352.         if ((!f_keep)
  353.             || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
  354.             if (chmod(skipcrud+head->header.name,
  355.                   notumask & (int)hstat.st_mode) < 0) {
  356.                 annofile(stderr, tar);
  357.                 perror(skipcrud+head->header.name);
  358.             }
  359.         }
  360. #endif
  361.  
  362.     quit:
  363.         break;
  364.  
  365.     case LF_LINK:
  366.     again_link:
  367.         check = link (head->header.linkname,
  368.                   skipcrud+head->header.name);
  369.         if (check == 0)
  370.             break;
  371.         if (make_dirs(skipcrud+head->header.name))
  372.             goto again_link;
  373.         annofile(stderr, tar);
  374.         fprintf(stderr, "Could not link %s to ",
  375.             skipcrud+head->header.name);
  376.         perror(head->header.linkname);
  377.         break;
  378.  
  379. #ifdef S_IFLNK
  380.     case LF_SYMLINK:
  381.     again_symlink:
  382.         check = symlink(head->header.linkname,
  383.                     skipcrud+head->header.name);
  384.         /* FIXME, don't worry uid, gid, etc... */
  385.         if (check == 0)
  386.             break;
  387.         if (make_dirs(skipcrud+head->header.name))
  388.             goto again_symlink;
  389.         annofile(stderr, tar);
  390.         fprintf(stderr, "Could not create symlink ");
  391.         perror(head->header.linkname);
  392.         break;
  393. #endif
  394.  
  395. #ifdef S_IFCHR
  396.     case LF_CHR:
  397.         hstat.st_mode |= S_IFCHR;
  398.         goto make_node;
  399. #endif
  400.  
  401. #ifdef S_IFBLK
  402.     case LF_BLK:
  403.         hstat.st_mode |= S_IFBLK;
  404.         goto make_node;
  405. #endif
  406.  
  407. #ifdef S_IFIFO
  408.     /* If local system doesn't support FIFOs, use default case */
  409.     case LF_FIFO:
  410.         hstat.st_mode |= S_IFIFO;
  411.         hstat.st_rdev = 0;        /* FIXME, do we need this? */
  412.         goto make_node;
  413. #endif
  414.  
  415.     make_node:
  416.         check = mknod(skipcrud+head->header.name,
  417.                   (int) hstat.st_mode, (int) hstat.st_rdev);
  418.         if (check != 0) {
  419.             if (make_dirs(skipcrud+head->header.name))
  420.                 goto make_node;
  421.             annofile(stderr, tar);
  422.             fprintf(stderr, "Could not make ");
  423.             perror(skipcrud+head->header.name);
  424.             break;
  425.         };
  426.         goto set_filestat;
  427.  
  428.     case LF_DIR:
  429.         namelen = strlen(skipcrud+head->header.name)-1;
  430.     really_dir:
  431.         /* Check for trailing /, and zap as many as we find. */
  432.         while (namelen && head->header.name[skipcrud+namelen] == '/')
  433.             head->header.name[skipcrud+namelen--] = '\0';
  434.         
  435.     again_dir:
  436. #ifdef AMIGA
  437.         /* don't mkdir("", mode) */
  438.         check = 0;
  439.         if (*(skipcrud+head->header.name))
  440. #endif
  441.         check = mkdir(skipcrud+head->header.name,
  442.                   0300 | (int)hstat.st_mode);
  443.         if (check != 0) {
  444.             if (make_dirs(skipcrud+head->header.name))
  445.                 goto again_dir;
  446.             /* If we're trying to create '.', let it be. */
  447.             if (head->header.name[skipcrud+namelen] == '.' && 
  448.                 (namelen==0 ||
  449.                  head->header.name[skipcrud+namelen-1]=='/'))
  450.                 goto check_perms;
  451.             annofile(stderr, tar);
  452.             fprintf(stderr, "Could not make directory ");
  453.             perror(skipcrud+head->header.name);
  454.             break;
  455.         }
  456.         
  457.     check_perms:
  458.         if (0300 != (0300 & (int) hstat.st_mode)) {
  459.             hstat.st_mode |= 0300;
  460.             annofile(stderr, tar);
  461.             fprintf(stderr,
  462.               "Added write & execute permission to directory %s\n",
  463.               skipcrud+head->header.name);
  464.         }
  465.  
  466.         goto set_filestat;
  467.         /* FIXME, Remember timestamps for after files created? */
  468.         /* FIXME, change mode after files created (if was R/O dir) */
  469.  
  470.     }
  471.  
  472.     /* We don't need to save it any longer. */
  473.     saverec((union record **) 0);    /* Unsave it */
  474. }
  475.  
  476. /*
  477.  * After a file/link/symlink/dir creation has failed, see if
  478.  * it's because some required directory was not present, and if
  479.  * so, create all required dirs.
  480.  */
  481. int
  482. make_dirs(pathname)
  483.     char *pathname;
  484. {
  485.     char *p;            /* Points into path */
  486.     int madeone = 0;        /* Did we do anything yet? */
  487.     int save_errno = errno;        /* Remember caller's errno */
  488.     int check;
  489.  
  490.     if (errno != ENOENT)
  491.         return 0;        /* Not our problem */
  492. #if defined(AMIGA) && defined(LATTICE)
  493.     /*
  494.      * ARRRRGGGGHHHH!  If delete bit is not set, open fails with ENOENT.
  495.      * I think it should be EPERM.  So we check for the file's existance
  496.      * here and return if it does exist.
  497.      */
  498.     if (!access(pathname, O_RDONLY))
  499.     {
  500.         errno = EPERM;        /* perror should say "Permission Denied"
  501.                      * but, says something stupid instead */
  502.         return(0);            /* Not our problem */
  503.     }
  504. #endif
  505.     for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
  506.         /* Avoid mkdir of empty string, if leading or double '/' */
  507.         if (p == pathname || p[-1] == '/')
  508.             continue;
  509.         /* Avoid mkdir where last part of path is '.' */
  510.         if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
  511.             continue;
  512.         *p = 0;                /* Truncate the path there */
  513.         check = mkdir (pathname, 0777);    /* Try to create it as a dir */
  514.         if (check == 0) {
  515.             /* Fix ownership */
  516.             if (we_are_root) {
  517.                 if (chown(pathname, hstat.st_uid,
  518.                       hstat.st_gid) < 0) {
  519.                     annofile(stderr, tar);
  520.                     perror(pathname);
  521.                 }
  522.             }
  523.             pr_mkdir(pathname, p-pathname, notumask&0777, stdout);
  524.             madeone++;        /* Remember if we made one */
  525.             *p = '/';
  526.             continue;
  527.         }
  528.         *p = '/';
  529.         if (errno == EEXIST)        /* Directory already exists */
  530.             continue;
  531.         /*
  532.          * Some other error in the mkdir.  We return to the caller.
  533.          */
  534.         break;
  535.     }
  536.  
  537.     errno = save_errno;        /* Restore caller's errno */
  538.     return madeone;            /* Tell them to retry if we made one */
  539. }
  540.